package com.zsy.springframework.beans.facotry.beanfactory;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.zsy.springframework.beans.facotry.aware.Aware;
import com.zsy.springframework.beans.facotry.aware.BeanClassLoaderAware;
import com.zsy.springframework.beans.facotry.aware.BeanFactoryAware;
import com.zsy.springframework.beans.facotry.aware.BeanNameAware;
import com.zsy.springframework.beans.facotry.config.BeanDefinition;
import com.zsy.springframework.beans.facotry.config.BeanReference;
import com.zsy.springframework.beans.facotry.config.PropertyValue;
import com.zsy.springframework.beans.facotry.config.PropertyValues;
import com.zsy.springframework.beans.facotry.exception.BeansException;
import com.zsy.springframework.beans.facotry.jiekou.one.BeanPostProcessor;
import com.zsy.springframework.beans.facotry.jiekou.one.DisposableBean;
import com.zsy.springframework.beans.facotry.jiekou.one.InitializingBean;
import com.zsy.springframework.beans.facotry.jiekou.two.AutowireCapableBeanFactory;
import com.zsy.springframework.beans.facotry.strategy.CglibSubclassingInstantiationStrategy;
import com.zsy.springframework.beans.facotry.strategy.InstantiationStrategy;
import com.zsy.springframework.beans.facotry.support.DisposableBeanAdapter;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

//根据名字，这个接口继承了顶级接口的实现类，然后又继承了同名的接口
//功能：实现bean的创建，属性填充，添加前后的postprocessor
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

    //创建bean的话，又涉及到策略 类了
    @Override
    protected Object createBean(BeanDefinition beanDefinition, String beanName, Object[] args) {
        //创建什么样子的bean呢？？？
        Object bean;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            //添加属性
            applyPropertyValues(beanName, bean, beanDefinition);
            //执行bean的初始化方法和 beanpostprocessor的前后处理器
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        addSingleton(beanName, bean);
        return bean;
    }

    private void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestoryMethodName())) {
            registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
        }
    }

    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        //invokeAwareMethods
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
        }
        if (bean instanceof BeanClassLoaderAware) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }

        //创建前置
        Object wrappedBean = applyBeanPostProcessorBeforeInitialization(bean, beanName);
        //调用初始化的方法 initmethod
        //这里先不展示
        try {
            invokeInitMethods(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
        }
        //执行后置处理
        wrappedBean = applyBeanPostProcessorAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }

    private void invokeInitMethods(String beanName, Object bean, BeanDefinition beanDefinition) throws Exception {
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }

        String initMethodName = beanDefinition.getInitMethodName();
        if (StrUtil.isNotEmpty(initMethodName) && !(bean instanceof InitializingBean)) {
            Method initMethod = beanDefinition.getBeanClass().getMethod(initMethodName);
            if (initMethod == null) {
                throw new BeansException("Could not find an init method named '" + initMethodName + "' on bean with name '" + beanName + "'");

            }
            initMethod.invoke(bean);
        }
    }

    protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                String name = propertyValue.getName();
                Object value = propertyValue.getValue();
                if (value instanceof BeanReference) {
                    //a 依赖b则获取b的实例
                    //todo
                    //这个先简单设置下，后续是需要修改的
                    //不用管
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                BeanUtil.setFieldValue(bean, name, value);

            }
        } catch (BeansException e) {
            throw new BeansException("Error setting property values：" + beanName);
        }
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        //通过反射
        Constructor useConstructor = null;
        Class beanClass = beanDefinition.getBeanClass();
        Constructor[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            if (args != null && declaredConstructor.getTypeParameters().length == args.length) {
                useConstructor = declaredConstructor;
                break;
            }
        }
        return getInstantiationStrategy().instantiation(beanDefinition, beanName, useConstructor, args);
    }


    //===========================================================
    //负责beanpostpreocessor
    @Override
    public Object applyBeanPostProcessorBeforeInitialization(Object bean, String beanName) throws BeansException {
        Object result = bean;
        for (BeanPostProcessor postProcessor : getPostProcessors()) {
            Object current = postProcessor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorAfterInitialization(Object bean, String beanName) throws BeansException {
        Object result = bean;
        for (BeanPostProcessor postProcessor : getPostProcessors()) {
            Object current = postProcessor.postProcessAfterInitialization(bean, beanName);
            if (current == null) return result;
            result = current;
        }
        return result;
    }
}
